package com.hrt.datasender.web.bdplccontrol;

import com.btcode.db.result.Row;
import com.btcode.db.sqlparameter.SQLParameterList;
import com.hrt.db.DBManager;
import com.hrt.util.DataHandleUtil;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

public final class BdPlcControlService {

    private static volatile BdPlcControlService instance;

    HashMap<String,String> systemStateDict = new HashMap<>();

    HashMap<String,String> runStateDict = new HashMap<>();

    private BdPlcControlService() {
        /**
         * 系统状态字典
         */
        systemStateDict.put("1.0","停止");
        systemStateDict.put("2.0","远程");
        systemStateDict.put("3.0","本地");
        /**
         * 运行状态字典
         */
        runStateDict.put("1.0","空闲");
        runStateDict.put("2.0","测量");
        runStateDict.put("3.0","标准");
        runStateDict.put("4.0","标样核查");
        runStateDict.put("5.0","清洗");
    }

    public static BdPlcControlService getInstance() {
        if (instance == null) {
            synchronized (BdPlcControlService.class) {
                if (instance == null) {
                    instance = new BdPlcControlService();
                }
            }
        }
        return instance;
    }

    /**
     * 1、获取设备表信息bc_devices
     * 2、根据设备里面的参数信息，和PLC点位配置信息去展示设备方框里面应该展示的数据
     * @return
     */
    public List<Row> getDeviceInfo() {
        String sql = "SELECT t.id, t.device_name FROM bc_devices t";
        List<Row> deviceRows = DBManager.getDataExecuter().queryList(sql,null);

        sql = "SELECT t.device_id,\n" +
                "       t1.PARAM_CODE_BUS,\n" +
                "       t1.POINT_NAME,\n" +
                "       t1.POINT_POSITION,\n " +
                "       t1.POINT_DATA_TYPE,\n" +
                "       t1.IS_COMBO_DISPLAY,\n" +
                "       t2.value,\n" +
                "       t2.updatetime\n" +
                "  FROM bc_device_monitor_param t\n" +
                "  LEFT JOIN bc_plc_point_config t1\n" +
                "    ON t1.param_code_bus = t.param_code_bus\n" +
                "  LEFT JOIN bd_plc_data_single t2\n" +
                "    ON t2.plc_point_id = t1.id\n" +
                " WHERE t1.IS_DISPLAY_MAIN = '0'\n" +
                " order by IF(ISNULL(t1.SHOW_ORDER),1,0), t1.show_order";
        List<Row> plcPointRows = DBManager.getDataExecuter().queryList(sql,null);
        plcPointRows = formatRowsToCombo(plcPointRows);
        plcPointRows = formatValue(plcPointRows);
        for(Row deviceRow:deviceRows){
            String deviceId = deviceRow.getSafeString("ID");
            List<Row> paramRows = DataHandleUtil.filter(plcPointRows,"DEVICE_ID",deviceId);
            deviceRow.setValue("PARAM",paramRows);
        }

        return deviceRows;
    }

    /**
     * 获取设备信息详情
     * @return
     */
    public List<Row> getDeviceDetailInfo(HashMap<String,String> parms) {
        String deviceId = parms.get("DEVICE_ID");
        String sql = "SELECT t.device_id,\n" +
            "       t1.PARAM_CODE_BUS,\n" +
            "       t1.POINT_NAME,\n" +
            "       t1.POINT_POSITION,\n " +
            "       t1.POINT_DATA_TYPE,\n" +
            "       t1.IS_COMBO_DISPLAY,\n" +
            "       t2.value,\n" +
            "       t2.updatetime\n" +
            "  FROM bc_device_monitor_param t\n" +
            "  LEFT JOIN bc_plc_point_config t1\n" +
            "    ON t1.param_code_bus = t.param_code_bus\n" +
            "  LEFT JOIN bd_plc_data_single t2\n" +
            "    ON t2.plc_point_id = t1.id\n" +
            " WHERE t1.IS_DISPLAY_DETAIL = '0'\n" +
            " and t.device_id = @DEVICE_ID" +
            " order by IF(ISNULL(t1.SHOW_ORDER),1,0), t1.show_order";
        SQLParameterList pc = new SQLParameterList();
        pc.put("DEVICE_ID",deviceId);
        List<Row> result = DBManager.getDataExecuter().queryList(sql,pc);
        result = formatRowsToCombo(result);
        result = formatValue(result);
        result = formatRowsToDisplay(result);
        return result;
    }

    /**
     * 获取系统信息
     * @return
     */
    public List<Row> getSystemInfo() {
        String sql = "SELECT t.POINT_NAME,\n " +
                "   t.POINT_POSITION,\n " +
                "   t.POINT_DATA_TYPE,\n" +
                "   t.IS_COMBO_DISPLAY,\n" +
                "   t1.value\n" +
                "  FROM bc_plc_point_config t\n" +
                "  LEFT JOIN bd_plc_data_single t1\n" +
                "    ON t1.plc_point_id = t.id\n" +
                " WHERE t.PARAM_CODE_BUS = 'system'\n" +
                "   AND t.IS_DISPLAY_MAIN = '0'\n" +
                " ORDER BY IF(ISNULL(t.SHOW_ORDER),1,0), t.SHOW_ORDER";
        List<Row> result = DBManager.getDataExecuter().queryList(sql,null);
        result = formatRowsToCombo(result);
        result = formatValue(result);
        result = formatRowsToDisplay(result);
        return result;
    }

    /**
     * 获取系统信息详情
     * @return
     */
    public List<Row> getSystemDetailInfo() {
        String sql = "SELECT t.POINT_NAME,\n " +
            "   t.POINT_POSITION,\n " +
            "   t.POINT_DATA_TYPE,\n" +
            "   t.IS_COMBO_DISPLAY,\n" +
            "   t1.value\n" +
            "  FROM bc_plc_point_config t\n" +
            "  LEFT JOIN bd_plc_data_single t1\n" +
            "    ON t1.plc_point_id = t.id\n" +
            " WHERE t.PARAM_CODE_BUS = 'system'\n" +
            "   AND t.IS_DISPLAY_DETAIL = '0'\n" +
            " ORDER BY IF(ISNULL(t.SHOW_ORDER),1,0), t.SHOW_ORDER";
        List<Row> result = DBManager.getDataExecuter().queryList(sql,null);
        result = formatRowsToCombo(result);
        result = formatValue(result);
        result = formatRowsToDisplay(result);
        return result;
    }

    /**
     * 1、系统状态、运行状态，进行字典翻译
     * 2、plc数据保留3位小数
     * @param rows
     * @return
     */
    private List<Row> formatValue(List<Row> rows){
        rows = translateRows(rows);
        DecimalFormat df = new DecimalFormat("0.000");
        for(Row row:rows){
            String value = row.getSafeString("VALUE");
            if(!"".equals(value)){
                try {
                    Double dValue = Double.parseDouble(value);
                    row.setValue("VALUE",df.format(dValue));
                }
                catch (Exception e){
                    /**
                     * 无法转换成double类型的，不作处理
                     */
                }
            }
        }
        return rows;
    }

    /**
     * plc数据组装成左右展示
     * @param rows
     * @return
     */
    private List<Row> formatRowsToDisplay(List<Row> rows){
        List<Row> result = new ArrayList<>();
        for (int i = 0; i < rows.size(); i++) {
            if(i%2==0){
                Row row = new Row();
                row.setValue("RIGHT", rows.get(i));
                if(i != rows.size()-1){
                    row.setValue("LEFT", rows.get(i+1));
                }
                result.add(row);
            }
        }
        return result;
    }

    /**
     * plc数据组合展示（bit类型数据进行组合展示）
     * @param rows
     * @return
     */
    private List<Row> formatRowsToCombo(List<Row> rows){
        List<Row> result = new ArrayList<>();
        List<Row> bitRows = DataHandleUtil.filter(rows,"POINT_DATA_TYPE","bit");
        for(Row row:rows){
            String isComboDisplay = row.getSafeString("IS_COMBO_DISPLAY");
            String pointDataType = row.getSafeString("POINT_DATA_TYPE");
            /**
             * 组合展示，当isComboDisplay为0时候进行组合展示
             * 1、根据点位位置，获取点位对应的组合数据
             * 2、将组合数据进行组装
             */
            if("0".equals(isComboDisplay)){
                String value = row.getSafeString("VALUE");
                if(!"0.0".equals(value)){
                    value = "故障:";
                    String pointPosition = row.getSafeString("POINT_POSITION");
                    List<Row> comboRows = getComboRows(bitRows,pointPosition);
                    if(comboRows.size()>0){
                        for(Row comboRow:comboRows){
                            String comboValue = comboRow.getSafeString("VALUE");
                            String comboPointName = comboRow.getSafeString("POINT_NAME");
                            if(!"0".equals(comboValue)){
                                value += comboPointName + "、";
                            }
                        }
                        value = value.substring(0,value.length()-1);
                    }
                }
                else{
                    value = "无故障";
                }
                row.setValue("VALUE",value);
            }

            if(!"bit".equals(pointDataType)){
                result.add(row);
            }
        }
        return result;
    }

    /**
     * 获取组合数据
     * @param rows
     * @param filterPosition
     * @return
     */
    private List<Row> getComboRows(List<Row> rows,String filterPosition){
        List<Row> result = new ArrayList<>();
        for(Row row:rows){
            String pointPosition = row.getSafeString("POINT_POSITION");
            if(pointPosition.contains(".")){
                String pointPositionInt = pointPosition.split("\\.")[0];
                if(filterPosition.equals(pointPositionInt)){
                    result.add(row);
                }
            }
        }
        return result;
    }

    /**
     * 字典翻译
     * @param rows
     * @return
     */
    private List<Row> translateRows(List<Row> rows){
        for(Row row:rows){
            String pointName = row.getSafeString("POINT_NAME");
            String value = row.getSafeString("VALUE");
            if("系统状态".equals(pointName)){
                value = systemStateDict.get(value);
                row.setValue("VALUE",value);
            }
            if("运行状态".equals(pointName)){
                value = runStateDict.get(value);
                row.setValue("VALUE",value);
            }

        }
        return rows;
    }


    /**
     * 创建PLC报警数据
     * 1、查询bd_plc_data_single表，查出报警相关点位数据
     * 2、查询bd_plc_alarm表，查出报警状态为0：正在报警的数据
     * 3、报警相关点位数据为1的情况：查询当前报警数据中是否存在对应报警，若存在则更新报警时间，若不存在则创建报警数据
     * 4、报警相关点位数据为0的情况：查询报警数据中是否存在对应报警，若存在则更新报警数据，若不存在则不进行操作
     */
    public void createPlcAlarm(){
        String sql = "SELECT t.PLC_POINT_ID, t.VALUE\n" +
                "  FROM bd_plc_data_single t\n" +
                "  LEFT JOIN bc_plc_point_config t1\n" +
                "    ON t1.id = t.PLC_POINT_ID\n" +
                " WHERE t1.IS_ALARM_POINT = '0'";
        List<Row> plcDataRows = DBManager.getDataExecuter().queryList(sql,null);

        sql = "SELECT t.* FROM bd_plc_alarm t WHERE t.ALARM_STATUS = '0'";
        List<BdPlcAlarmModel> plcAlarmModels = DBManager.getORMExecuter()
            .getModelListSQL(sql,null,BdPlcAlarmModel.class);

        for(Row plcDataRow:plcDataRows){
            String plcPointId = plcDataRow.getSafeString("PLC_POINT_ID");
            String value = plcDataRow.getSafeString("VALUE");
            BdPlcAlarmModel plcAlarmModel = getPlcAlarmModel(plcAlarmModels,plcPointId);
            /**
             * 报警相关点位数据为1的情况
             */
            if("1".equals(value)){
                if(plcAlarmModel==null){
                    /**
                     * 不存在则创建报警数据
                     */
                    plcAlarmModel = new BdPlcAlarmModel();
                    plcAlarmModel.setId(UUID.randomUUID().toString());
                    plcAlarmModel.setAlarmStatus("0");
                    plcAlarmModel.setPlcPointId(plcPointId);
                    DBManager.getORMExecuter().create(plcAlarmModel);
                }
                else{
                    /**
                     * 存在则更新报警时间，
                     */
                    plcAlarmModel.setUpdatetime(new Date());
                    DBManager.getORMExecuter().update(plcAlarmModel);
                }
            }

            /**
             * 报警相关点位数据为0的情况
             */
            if("0".equals(value)){
                if(plcAlarmModel!=null){
                    plcAlarmModel.setAlarmStatus("1");
                    plcAlarmModel.setEndTime(new Date());
                    plcAlarmModel.setUpdatetime(new Date());
                    DBManager.getORMExecuter().update(plcAlarmModel);
                }
            }
        }
    }

    private BdPlcAlarmModel getPlcAlarmModel(List<BdPlcAlarmModel> plcAlarmModels,String plcPointId){
        BdPlcAlarmModel model = null;
        for(BdPlcAlarmModel plcAlarmModel:plcAlarmModels){
            if(plcPointId.equals(plcAlarmModel.getPlcPointId())){
                model = plcAlarmModel;
            }
        }
        return model;
    }

    /**
     * 获取PLC报警数据
     * @return
     */
    public List<Row> getPlcAlarm(HashMap<String,String> parms) {
        String paramCodeBus = parms.get("PARAM_CODE_BUS");
        String alarmStatus = parms.get("ALARM_STATUS");
        String start = parms.get("START");
        String end = parms.get("END");
        String sql = "SELECT t2.PARAM_NAME,\n" +
            "       t1.PARAM_CODE_BUS,\n" +
            "       t1.POINT_NAME,\n" +
            "       t1.POINT_POSITION,\n" +
            "       t.CREATETIME,\n" +
            "       t.END_TIME,\n" +
            "       t.UPDATETIME,\n" +
            "       t.ALARM_STATUS\n" +
            "  FROM bd_plc_alarm t\n" +
            "  LEFT JOIN bc_plc_point_config t1\n" +
            "    ON t1.id = t.PLC_POINT_ID\n" +
            "  LEFT JOIN bc_monitor_param_dict t2\n" +
            "    ON t2.PARAM_CODE_BUS = t1.PARAM_CODE_BUS\n" +
            " WHERE 1 = 1\n" +
            " AND t.CREATETIME > @START\n" +
            " AND t.CREATETIME < @END\n";
        SQLParameterList pc = new SQLParameterList();
        pc.put("START",start);
        pc.put("END",end);
        if(paramCodeBus!=null&&!"all".equals(paramCodeBus)){
            sql += " and t1.PARAM_CODE_BUS = @PARAM_CODE_BUS";
            pc.put("PARAM_CODE_BUS",paramCodeBus);
        }
        if(alarmStatus!=null&&!"all".equals(alarmStatus)){
            sql += " and t.ALARM_STATUS = @ALARM_STATUS";
            pc.put("ALARM_STATUS",alarmStatus);
        }
        sql += " order by t.createtime desc";
        List<Row> result = DBManager.getDataExecuter().queryList(sql,pc);
        return result;
    }

    public List<Row> getParamByDevice(HashMap<String,String> parms) {
        String deviceId = parms.get("DEVICE_ID");
        String sql = "SELECT t.PARAM_CODE_BUS\n" +
                "  FROM bc_device_monitor_param t\n" +
                " WHERE t.DEVICE_ID = @DEVICE_ID";
        SQLParameterList pc = new SQLParameterList();
        pc.put("DEVICE_ID",deviceId);
        List<Row> rows = DBManager.getDataExecuter().queryList(sql,pc);
        return rows;
    }
}
